package com.zqll.gdx.starter.assets.font;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeFontParameter;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeBitmapFontData;
import com.badlogic.gdx.utils.Disposable;
import com.badlogic.gdx.utils.ObjectMap;
import com.zqll.gdx.starter.assets.utils.TextUtils;

/**
 * TTF字体生成器
 * 支持从TTF字体文件生成LibGDX的BitmapFont，支持增量字体生成和字符缓存
 */
public class FontGenerator implements Disposable {
    
    private FreeTypeFontGenerator generator;
    private String fontPath;
    private ObjectMap<Object, BitmapFont> fontCache;
    
    // 缓存不同路径的临时生成器，避免过早释放导致增量字体崩溃
    private ObjectMap<String, FreeTypeFontGenerator> generatorCache;

    /**
     * 初始化字体生成器
     * @param fontPath 字体文件路径
     * @param defaultSize 默认字体大小
     */
    public void initialize(String fontPath, int defaultSize) {
        this.fontPath = fontPath;
        this.fontCache = new ObjectMap<>();
        this.generatorCache = new ObjectMap<>();

        FileHandle fontFile = findFontFile(fontPath);
        if (fontFile == null || !fontFile.exists()) {
            throw new RuntimeException("找不到可用中文字体文件: " + fontPath);
        }

        // 将主字体生成器也存储到缓存中，实现统一管理
        String actualFontPath = fontFile.path();
        generator = getOrCreateGenerator(actualFontPath, fontFile);
        this.fontPath = actualFontPath; // 更新为实际路径
        
        Gdx.app.log("FontGenerator", "字体生成器初始化成功: " + fontFile.path());
    }

    /**
     * 查找字体文件，支持internal和classpath两种模式
     * @param fontPath 字体文件路径
     * @return 找到的字体文件，如果未找到则返回null
     */
    private FileHandle findFontFile(String fontPath) {
        // 尝试指定路径
        if (fontPath != null) {
            if (Gdx.files.internal(fontPath).exists()) {
                return Gdx.files.internal(fontPath);
            } else if (Gdx.files.classpath(fontPath).exists()) {
                return Gdx.files.classpath(fontPath);
            } else if (fontPath.startsWith("assets/")) {
                String stripped = fontPath.substring("assets/".length());
                if (Gdx.files.internal(stripped).exists()) {
                    return Gdx.files.internal(stripped);
                }
            }
        }

        // 尝试默认字体
        String primary = com.zqll.gdx.starter.assets.AssetPaths.FONT_MAPLE_MONO_SC;
        if (Gdx.files.internal(primary).exists()) {
            return Gdx.files.internal(primary);
        } else {
            String cp = com.zqll.gdx.starter.assets.AssetPaths.classpath(primary);
            if (Gdx.files.classpath(cp).exists()) {
                return Gdx.files.classpath(cp);
            }
        }

        // 尝试备用字体
        String fallback = com.zqll.gdx.starter.assets.AssetPaths.FONT_NOTO_MONO_CJK;
        if (Gdx.files.internal(fallback).exists()) {
            return Gdx.files.internal(fallback);
        } else {
            String cpFallback = com.zqll.gdx.starter.assets.AssetPaths.classpath(fallback);
            if (Gdx.files.classpath(cpFallback).exists()) {
                return Gdx.files.classpath(cpFallback);
            }
        }

        return null;
    }
    
    /**
     * 生成指定大小的中文字体（基础字符集）
     * @param size 字体大小
     * @return 生成的字体
     */
    public BitmapFont generateFont(int size) {
        return generateFont(size, Color.WHITE, false);
    }

    public BitmapFont generateFont(int size, boolean hdScale) {
        return generateFont(size, Color.WHITE, hdScale);
    }

    /**
     * 生成指定大小和颜色的中文字体（基础字符集）
     * @param size 字体大小
     * @param color 字体颜色
     * @return 生成的字体
     */
    public BitmapFont generateFont(int size, Color color, boolean hdScale) {
        FreeTypeFontParameter parameter = new FreeTypeFontParameter();
        parameter.size = size;
        parameter.color = color;
        parameter.incremental = true;
        parameter.minFilter = com.badlogic.gdx.graphics.Texture.TextureFilter.Linear;
        parameter.magFilter = com.badlogic.gdx.graphics.Texture.TextureFilter.Linear;
        return generateFont(parameter, hdScale);
    }
    
    /**
     * 为指定文本生成增量字体
     * 只包含文本中实际使用的字符
     * @param size 字体大小
     * @param text 需要支持的文本内容
     * @return 生成的字体
     */
    public BitmapFont generateIncrementalFont(int size, String text) {
        return generateIncrementalFont(size, text, Color.WHITE);
    }
    
    /**
     * 为指定文本生成增量字体
     * @param size 字体大小
     * @param text 需要支持的文本内容
     * @param color 字体颜色
     * @return 生成的字体
     */
    public BitmapFont generateIncrementalFont(int size, String text, Color color) {
        if (generator == null) {
            throw new IllegalStateException("字体生成器未初始化");
        }
        String uniqueChars = TextUtils.extractUniqueChars(text);
        FreeTypeFontParameter parameter = new FreeTypeFontParameter();
        parameter.size = size;
        parameter.color = color;
        parameter.incremental = true;
        parameter.minFilter = com.badlogic.gdx.graphics.Texture.TextureFilter.Linear;
        parameter.magFilter = com.badlogic.gdx.graphics.Texture.TextureFilter.Linear;
        // 暂时走统一入口，缓存键由参数决定；如果需要针对 uniqueChars 缓存，可在后续键中加入
        BitmapFont font = generateFont(parameter);
        Gdx.app.log("FontGenerator", "生成增量字体，大小: " + size + ", 字符数: " + uniqueChars.length());
        return font;
    }

    /**
     * 清除字体缓存
     */
    public void clearCache() {
        for (BitmapFont font : fontCache.values()) {
            font.dispose();
        }
        fontCache.clear();
        Gdx.app.log("FontGenerator", "字体缓存已清除");
    }
    
    /**
     * 生成包含指定文本所有字符的字体
     * 适用于已知文本内容的情况，可以减少字体文件大小
     * @param size 字体大小
     * @param text 需要支持的文本内容
     * @return 生成的字体
     */
    public BitmapFont generateFontForText(int size, String text) {
        if (generator == null) {
            throw new IllegalStateException("字体生成器未初始化");
        }
        
        FreeTypeFontParameter parameter = new FreeTypeFontParameter();
        parameter.size = size;
        parameter.color = Color.WHITE;
        parameter.incremental = true;
        
        // 提取文本中的唯一字符
        StringBuilder uniqueChars = new StringBuilder();
        uniqueChars.append(FreeTypeFontGenerator.DEFAULT_CHARS); // 包含基本字符
        
        for (char c : text.toCharArray()) {
            if (uniqueChars.indexOf(String.valueOf(c)) == -1) {
                uniqueChars.append(c);
            }
        }
        
        parameter.characters = uniqueChars.toString();
        parameter.minFilter = com.badlogic.gdx.graphics.Texture.TextureFilter.Linear;
        parameter.magFilter = com.badlogic.gdx.graphics.Texture.TextureFilter.Linear;
        
        return generateFont(parameter);
    }

    /** 基于当前默认生成器生成字体，应用HD缩放、中文x/cap、换行策略与缓存。 */
    public BitmapFont generateFont(FreeTypeFontParameter parameter) {
        return generateFont(parameter, true);
    }

    /** 基于当前默认生成器生成字体（可切换 HD 缩放）。 */
    public BitmapFont generateFont(FreeTypeFontParameter parameter, boolean hdScale) {
        if (generator == null) {
            throw new IllegalStateException("字体生成器未初始化");
        }
        ParamKey key = new ParamKey(parameter, null, fontPath, hdScale);
        if (fontCache.containsKey(key)) {
            return fontCache.get(key);
        }
        BitmapFont font = generateWith(generator, parameter, hdScale);
        fontCache.put(key, font);
        return font;
    }

    /** 使用指定路径创建临时生成器生成字体（支持 internal/classpath），生成后释放。 */
    public BitmapFont generateFont(FreeTypeFontParameter parameter, String path) {
        return generateFont(parameter, path, true);
    }

    /** 使用指定路径创建临时生成器生成字体（支持 internal/classpath），生成后释放，可切换 HD 缩放。 */
    public BitmapFont generateFont(FreeTypeFontParameter parameter, String path, boolean hdScale) {
        FileHandle fontFile = findFontFile(path);
        if (fontFile == null) {
            throw new RuntimeException("找不到可用中文字体文件: " + path);
        }
        
        // 使用缓存的临时生成器，避免过早释放导致增量字体崩溃
        String fontPath = fontFile.path();
        FreeTypeFontGenerator tempGen = getOrCreateGenerator(fontPath, fontFile);
        
        ParamKey key = new ParamKey(parameter, null, path, hdScale);
        if (fontCache.containsKey(key)) return fontCache.get(key);
        BitmapFont font = generateWith(tempGen, parameter, hdScale);
        fontCache.put(key, font);
        return font;
    }

    /**
     * 获取或创建字体生成器，支持缓存
     * 实现fontPath-generator的统一映射管理
     * @param fontPath 字体路径
     * @param fontFile 字体文件句柄
     * @return 字体生成器
     */
    private FreeTypeFontGenerator getOrCreateGenerator(String fontPath, FileHandle fontFile) {
        FreeTypeFontGenerator tempGen = generatorCache.get(fontPath);
        if (tempGen == null) {
            tempGen = new FreeTypeFontGenerator(fontFile);
            generatorCache.put(fontPath, tempGen);
            Gdx.app.log("FontGenerator", "创建并缓存字体生成器: " + fontPath);
        }
        return tempGen;
    }

    /**
     * 获取当前缓存的生成器数量（用于调试和监控）
     * @return 缓存的生成器数量
     */
    public int getCachedGeneratorCount() {
        return generatorCache != null ? generatorCache.size : 0;
    }

    /**
     * 清除指定路径的生成器缓存
     * @param fontPath 字体路径
     */
    public void clearGeneratorCache(String fontPath) {
        if (generatorCache != null && generatorCache.containsKey(fontPath)) {
            FreeTypeFontGenerator gen = generatorCache.remove(fontPath);
            if (gen != null && gen != generator) { // 避免释放主生成器
                gen.dispose();
                Gdx.app.log("FontGenerator", "清除字体生成器缓存: " + fontPath);
            }
        }
    }

    /** 统一生成实现：应用HD缩放、x/cap中文字符、换行策略。 */
    private static BitmapFont generateWith(FreeTypeFontGenerator gen, FreeTypeFontParameter param, boolean hdScale) {
        FreeTypeFontParameter hd = clone(param);
        if (hd.size <= 0) hd.size = 16;
        if (hdScale) {
            hd.size = Math.max(1, Math.round(hd.size * 2f));
            hd.borderWidth *= 2f;
            hd.shadowOffsetX = Math.round(hd.shadowOffsetX * 2f);
            hd.shadowOffsetY = Math.round(hd.shadowOffsetY * 2f);
            hd.spaceX = Math.round(hd.spaceX * 2f);
            hd.spaceY = Math.round(hd.spaceY * 2f);
            hd.padTop = Math.round(hd.padTop * 2f);
            hd.padLeft = Math.round(hd.padLeft * 2f);
            hd.padBottom = Math.round(hd.padBottom * 2f);
            hd.padRight = Math.round(hd.padRight * 2f);
        }
        FreeTypeBitmapFontData data = new FreeTypeBitmapFontData() {
            public int getWrapIndex(com.badlogic.gdx.utils.Array<BitmapFont.Glyph> glyphs, int start) {
                return SimplifiedChinese.getWrapIndex(glyphs, start);
            }
        };

        FreeTypeFontGenerator.setMaxTextureSize(1024);
        BitmapFont font = gen.generateFont(hd/*, data*/);
        if (hdScale) font.getData().setScale(1f/2f);
        return font;
    }

    /** 克隆参数对象，避免外部修改影响缓存比较。 */
    public static FreeTypeFontParameter clone(FreeTypeFontParameter original) {
        FreeTypeFontParameter copy = new FreeTypeFontParameter();
        copy.size = original.size;
        copy.mono = original.mono;
        copy.hinting = original.hinting;
        copy.color = new Color(original.color);
        copy.gamma = original.gamma;
        copy.renderCount = original.renderCount;
        copy.borderWidth = original.borderWidth;
        copy.borderColor = new Color(original.borderColor);
        copy.borderStraight = original.borderStraight;
        copy.borderGamma = original.borderGamma;
        copy.shadowOffsetX = original.shadowOffsetX;
        copy.shadowOffsetY = original.shadowOffsetY;
        copy.shadowColor = new Color(original.shadowColor);
        copy.spaceX = original.spaceX;
        copy.spaceY = original.spaceY;
        copy.padTop = original.padTop;
        copy.padLeft = original.padLeft;
        copy.padBottom = original.padBottom;
        copy.padRight = original.padRight;
        copy.characters = original.characters;
        copy.kerning = original.kerning;
        copy.packer = null;
        copy.flip = original.flip;
        copy.genMipMaps = original.genMipMaps;
        copy.minFilter = original.minFilter;
        copy.magFilter = original.magFilter;
        copy.incremental = original.incremental;
        return copy;
    }

    @Override
    public void dispose() {
        // 释放所有缓存的字体
        if (fontCache != null) {
            for (BitmapFont bitmapFont : fontCache.values()) {
                if (bitmapFont != null) {
                    bitmapFont.dispose();
                }
            }
            fontCache.clear();
        }

        if (generator != null) {
            generator = null;
            Gdx.app.log("FontGenerator", "字体生成器已释放");
        }

        // 释放所有缓存的临时生成器
        if (generatorCache != null) {
            for (FreeTypeFontGenerator tempGen : generatorCache.values()) {
                if (tempGen != null) {
                    tempGen.dispose();
                }
            }
            generatorCache.clear();
            Gdx.app.log("FontGenerator", "临时字体生成器缓存已清理");
        }
    }

    /** 参数缓存键：基于参数关键字段 + 额外字符集或字体路径。 */
    private static class ParamKey {
        final FreeTypeFontParameter param;
        final String extraChars;
        final String fontPath;
        final boolean hdScale;
        ParamKey(FreeTypeFontParameter p, String chars, String path, boolean hdScale) {
            this.param = FontGenerator.clone(p);
            this.extraChars = chars;
            this.fontPath = path;
            this.hdScale = hdScale;
        }
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            ParamKey k = (ParamKey) o;
            if (hdScale != k.hdScale) return false;
            if (param.size != k.param.size) return false;
            if (param.mono != k.param.mono) return false;
            if (!param.color.equals(k.param.color)) return false;
            if (Float.compare(param.gamma, k.param.gamma) != 0) return false;
            if (Float.compare(param.borderWidth, k.param.borderWidth) != 0) return false;
            if (param.borderStraight != k.param.borderStraight) return false;
            if (Float.compare(param.borderGamma, k.param.borderGamma) != 0) return false;
            if (param.shadowOffsetX != k.param.shadowOffsetX) return false;
            if (param.shadowOffsetY != k.param.shadowOffsetY) return false;
            if (param.spaceX != k.param.spaceX) return false;
            if (param.spaceY != k.param.spaceY) return false;
            if (param.padTop != k.param.padTop) return false;
            if (param.padLeft != k.param.padLeft) return false;
            if (param.padBottom != k.param.padBottom) return false;
            if (param.padRight != k.param.padRight) return false;
            if (param.flip != k.param.flip) return false;
            if (param.genMipMaps != k.param.genMipMaps) return false;
            if (param.hinting != k.param.hinting) return false;
            if (param.minFilter != k.param.minFilter) return false;
            if (param.magFilter != k.param.magFilter) return false;
            if (param.incremental != k.param.incremental) return false;
            if (extraChars != null ? !extraChars.equals(k.extraChars) : k.extraChars != null) return false;
            return fontPath != null ? fontPath.equals(k.fontPath) : k.fontPath == null;
        }
        @Override
        public int hashCode() {
            int result = param.size;
            result = 31 * result + (hdScale ? 1 : 0);
            result = 31 * result + (param.mono ? 1 : 0);
            result = 31 * result + param.color.hashCode();
            result = 31 * result + (param.gamma != +0.0f ? Float.floatToIntBits(param.gamma) : 0);
            result = 31 * result + (param.borderWidth != +0.0f ? Float.floatToIntBits(param.borderWidth) : 0);
            result = 31 * result + (param.borderStraight ? 1 : 0);
            result = 31 * result + (param.borderGamma != +0.0f ? Float.floatToIntBits(param.borderGamma) : 0);
            result = 31 * result + param.shadowOffsetX;
            result = 31 * result + param.shadowOffsetY;
            result = 31 * result + param.spaceX;
            result = 31 * result + param.spaceY;
            result = 31 * result + param.padTop;
            result = 31 * result + param.padLeft;
            result = 31 * result + param.padBottom;
            result = 31 * result + param.padRight;
            result = 31 * result + (param.flip ? 1 : 0);
            result = 31 * result + (param.genMipMaps ? 1 : 0);
            result = 31 * result + (param.hinting != null ? param.hinting.hashCode() : 0);
            result = 31 * result + (param.minFilter != null ? param.minFilter.hashCode() : 0);
            result = 31 * result + (param.magFilter != null ? param.magFilter.hashCode() : 0);
            result = 31 * result + (param.incremental ? 1 : 0);
            result = 31 * result + (extraChars != null ? extraChars.hashCode() : 0);
            result = 31 * result + (fontPath != null ? fontPath.hashCode() : 0);
            return result;
        }
    }

    /** 简体中文换行策略（简化自 DynamicBitmapFont）。 */
    private static class SimplifiedChinese {
        public static int getWrapIndex(com.badlogic.gdx.utils.Array<BitmapFont.Glyph> glyphs, int start) {
            int i = start - 1;
            for (; i >= 1; i--) {
                int startChar = glyphs.get(i).id;
                if (!legalAtStart(startChar)) continue;
                int endChar = glyphs.get(i - 1).id;
                if (!legalAtEnd(endChar)) continue;
                if (startChar < 127 && endChar < 127 && !Character.isWhitespace(startChar)) continue;
                return i;
            }
            return start;
        }

        private static final String END_ILLEGAL = "([{《“‘「『（［｛〔﹝〖【‵〈﹙｟⦅⦑｢﹁";
        private static final String START_ILLEGAL = ")]}》”’」』）］｝〕﹞〗】′〈﹚｠⦆⦒｣﹂、。，：；？！…";

        private static boolean legalAtEnd(int ch) {
            return END_ILLEGAL.indexOf(ch) < 0;
        }
        private static boolean legalAtStart(int ch) {
            return START_ILLEGAL.indexOf(ch) < 0;
        }
    }
}